#include "ProcessingGraph.h"
#include <stack>
#include <QDebug>
#include <opencv2\core\core.hpp>
#include <QGroupBox>

ProcessingGraph::ProcessingGraph(void) : running(false)
{
	QObject::connect(&fpsTimer, SIGNAL(timeout()), this, SLOT(SendFPS()));

	//UI
	widget = new QWidget();	
	
	widgetLayout = new QHBoxLayout();
	filtersLayout = new QHBoxLayout();
	propertiesLayout = new QVBoxLayout();

	widgetLayout->addLayout(propertiesLayout);
	widgetLayout->addLayout(filtersLayout);
	widget->setLayout(widgetLayout);
}


ProcessingGraph::~ProcessingGraph(void)
{
	Stop();
}

void ProcessingGraph::AddFilter(Filter &filter)
{
	filters.push_back(&filter);
}

void ProcessingGraph::Compile()
{
	//clear current
	computingOrder.clear();

	FiltersList computingOrderTemp;

	//find outputs
	std::stack<Filter*> toProcess;

	for (FiltersVector::const_iterator it = filters.begin(); it != filters.end(); ++it)
	{
		toProcess.push(*it);
	}

	while(!toProcess.empty())
	{
		Filter *current = toProcess.top();
		toProcess.pop();

		computingOrderTemp.push_front(current);
		for (int i = 0; i < current->GetInputsCount(); ++i)
		{
			Filter *inputFilter = current->GetInputFilter(i);
			if (inputFilter)
			{
				if (std::find(filters.begin(), filters.end(), inputFilter) == filters.end())
				{
					throw ProcessingGraphException("Filter out of this processing graph");
				}

				toProcess.push(inputFilter);
			}
		}
	}

	for (FiltersList::iterator it = computingOrderTemp.begin(); it != computingOrderTemp.end(); ++it)
	{
		bool contains = false;
		for (FiltersList::iterator added = computingOrder.begin(); added != computingOrder.end(); ++added)
		{
			if ((*added) == (*it)) {
				contains = true;
				break;
			}
		}

		if (!contains)
		{
			computingOrder.push_back(*it);
			QWidget *properties = (*it)->GetPropertiesWidget();
			QWidget *outputWidget = (*it)->GetOutputWidget();

			if (properties)
			{
				QGroupBox *group = new QGroupBox((*it)->GetFilterName());
				QVBoxLayout *groupLayout = new QVBoxLayout();
				groupLayout->addWidget(properties);
				group->setLayout(groupLayout);
				propertiesLayout->addWidget(group);
			}


			if (outputWidget)
			{
				filtersLayout->addWidget(outputWidget);
			}
		}
	}

	qDebug() << "Filters count: " << computingOrder.size();
	for (auto it = computingOrder.begin(); it != computingOrder.end(); ++it)
	{
		qDebug() << (*it)->GetFilterName();
	}
}

void ProcessingGraph::Start()
{
	runningMutex.lock();
	running = true;
	this->start();
	fpsTimer.start(1000);
	frames = 0;
	runningMutex.unlock();
}

void ProcessingGraph::run()
{
	int iteration = 0;

	while(true)
	{
		++iteration;
		for (FiltersVector::iterator it = filters.begin(); it != filters.end(); ++it)
			(*it)->NextFrame();

		for (FiltersList::iterator it = computingOrder.begin(); it != computingOrder.end(); ++it)
		{
			(*it)->RunProcess();
			if ((*it)->GetEndOfStream())
			{
				this->Stop();
				emit EndOfStream();
				break;
			}
		}

		runningMutex.lock();
		if (!running)
		{
			runningMutex.unlock();
			break;
		}
		runningMutex.unlock();

		framesMutex.lock();
		++frames;
		framesMutex.unlock();
	}
}

void ProcessingGraph::Stop()
{
	runningMutex.lock();
	running = false;
	runningMutex.unlock();
	fpsTimer.stop();

	qDebug() << "Stop";
}

void ProcessingGraph::SendFPS()
{
	framesMutex.lock();
	int f = frames;
	frames = 0;
	framesMutex.unlock();

	emit Fps(QString("FPS: %1").arg(f));
}

QWidget *ProcessingGraph::GetWidget()
{
	return widget;
}

void ProcessingGraph::LoadFromXML(const QString &filename)
{

}

void ProcessingGraph::SaveToXML(const QString &filename)
{

}
